%{

#include "pytool.hpp"

namespace kuai { namespace suan {


boost::tuple<PyObject*, RealNumber*, size_t> getRealArray(PyObject* model, const char szAttrName[]) {
	static const boost::tuple<PyObject*, RealNumber*, size_t> RESULT_NONE(NULL, NULL, 0);
	PyObject* obj = PyObject_GetAttrString(model, szAttrName);
	if (obj == NULL) {
		return RESULT_NONE;
	}
	if (!PyArray_Check(obj)) {
		PyArray_Descr* descr = PyArray_DescrFromType(NPY_REAL_TYPE);
		PyObject* arrayobj = PyArray_FromAny(obj, descr, 0, 0, NPY_C_CONTIGUOUS, NULL);
		if (arrayobj == NULL) {
			Py_DECREF(obj);
			return RESULT_NONE;
		}
		if (PyObject_SetAttrString(model, szAttrName, arrayobj) == -1) {
			Py_DECREF(arrayobj);
			Py_DECREF(obj);
			return RESULT_NONE;
		};
		Py_DECREF(obj);
		obj = arrayobj;
	}
	std::pair<RealNumber*, size_t> pn = getRealArray(obj);
	return boost::make_tuple(obj, pn.first, pn.second);
}

std::pair<RealNumber*, size_t> getRealArray(PyObject* obj) {
	static const std::pair<RealNumber*, size_t> RESULT_NONE(NULL, 0);
	if (PyArray_NDIM(obj) != 1 || PyArray_TYPE(obj) != NPY_REAL_TYPE) {
		PyErr_SetString(PyExc_ValueError, "Want an 1D Real Array");
		return RESULT_NONE;
	}
	if (!PyArray_ISCONTIGUOUS(obj)) {
		PyErr_SetString(PyExc_ValueError, "Need a contiguous array");
		return RESULT_NONE;
	}
	size_t n = PyArray_DIM(obj, 0);
	RealNumber* p = reinterpret_cast<RealNumber*>(PyArray_DATA(obj));
	return std::make_pair(p, n);
};

boost::tuple<PyObject*, XYZ*, size_t> getXYZArray(PyObject* model, const char szAttrName[]) {
	static const boost::tuple<PyObject*, XYZ*, size_t> RESULT_NONE(NULL, NULL, 0);
	boost::tuple<PyObject*, RealNumber*, size_t> result = getRealArray(model, szAttrName);
	PyObject* obj = boost::get<0>(result);
	if (obj == NULL) {
		return RESULT_NONE;
	}
	size_t n = boost::get<2>(result); 
	if ((n % 3) != 0) {
		PyErr_SetString(PyExc_ValueError, "The length of the array can not divide by 3");
		return RESULT_NONE;
	}
	XYZ* p = reinterpret_cast<XYZ*>(boost::get<1>(result));
	return boost::make_tuple(obj, p, n / 3);
};

std::pair<XYZ*, size_t> getXYZArray(PyObject* obj) {
	static const std::pair<XYZ*, size_t> RESULT_NONE(NULL, 0);
	static const std::pair<RealNumber*, size_t> result = getRealArray(obj);
	if (result.first == NULL) {
		return RESULT_NONE;
	}
	if ((result.second % 3) != 0) {
		PyErr_SetString(PyExc_ValueError, "The length of the array can not divide by 3");
		return RESULT_NONE;
	}
	XYZ* p = reinterpret_cast<XYZ*>(result.first);
	return std::make_pair(p, result.second / 3);

};

boost::tuple<PyObject*, Index*, size_t> getIndexArray(PyObject* model, const char szAttrName[]) {
	static const boost::tuple<PyObject*, Index*, size_t> RESULT_NONE(NULL, NULL, 0);
	PyObject* obj = PyObject_GetAttrString(model, szAttrName);
	if (obj == NULL) {
		return RESULT_NONE;
	}
	if (!PyArray_Check(obj)) {
		PyArray_Descr* descr = PyArray_DescrFromType(NPY_INDEX_TYPE);
		PyObject* arrayobj = PyArray_FromAny(obj, descr, 0, 0, NPY_C_CONTIGUOUS, NULL);
		if (arrayobj == NULL) {
			Py_DECREF(obj);
			return RESULT_NONE;
		}
		if (PyObject_SetAttrString(model, szAttrName, arrayobj) == -1) {
			Py_DECREF(arrayobj);
			Py_DECREF(obj);
			return RESULT_NONE;
		};
		Py_DECREF(obj);
		obj = arrayobj;
	}
	std::pair<Index*, size_t> pn = getIndexArray(obj);
	return boost::make_tuple(obj, pn.first, pn.second);
}
std::pair<Index*, size_t> getIndexArray(PyObject* obj) {
	static const std::pair<Index*, size_t> RESULT_NONE(NULL, 0);
	if (PyArray_NDIM(obj) != 1 || PyArray_TYPE(obj) != NPY_INDEX_TYPE) {
		PyErr_SetString(PyExc_ValueError, "Want an 1D Index Array");
		return RESULT_NONE;
	}
	if (!PyArray_ISCONTIGUOUS(obj)) {
		PyErr_SetString(PyExc_ValueError, "Need a contiguous array");
		return RESULT_NONE;
	}
	size_t n = PyArray_DIM(obj, 0);
	Index* p = reinterpret_cast<Index*>(PyArray_DATA(obj));
	return std::make_pair(p, n);
}

std::pair<PyObject*, RealNumber*> newRealArray(npy_intp n) {
	PyArray_Descr* descr = PyArray_DescrFromType(NPY_REAL_TYPE);
	std::pair<PyObject*, RealNumber*> result(NULL, NULL);
	result.first = PyArray_SimpleNewFromDescr(1, &n, descr);
	if (result.first != NULL) {
		result.second = reinterpret_cast<RealNumber*>(PyArray_DATA(result.first));
	}
	return result;
}

std::pair<PyObject*, RealNumber*> newRealArray(npy_intp n, RealNumber v0) {
	std::pair<PyObject*, RealNumber*> result = newRealArray(n);
	if (result.first != NULL) {
		std::fill(result.second, result.second+n, v0);
	}
	return result;
}

std::pair<PyObject*, RealNumber*> newRealArray(PyObject* model, const char szAttrName[], size_t n) {
	if (PyObject_HasAttrString(model, szAttrName)) {
		boost::tuple<PyObject*, kuai::RealNumber*, size_t> 
			result = getRealArray(model, szAttrName);
		if (boost::get<0>(result)) {
			if (boost::get<2>(result) == n) {
				return std::make_pair(boost::get<0>(result), boost::get<1>(result));
			}
			else {
				Py_DECREF(boost::get<0>(result));
			}
		}
		else {
			PyErr_Clear();
		}
	}
	std::pair<PyObject*, RealNumber*> result = newRealArray(n);
	if (result.first) {
		if (PyObject_SetAttrString(model, szAttrName, result.first) == -1)
		{ 
			Py_DECREF(result.first);
			result.first = NULL;
			result.second = NULL;
		}
	}
	return result;

}


std::pair<PyObject*, XYZ*> newXYZArray(npy_intp n) {
	std::pair<PyObject*, RealNumber*> v = newRealArray(n*3);
	XYZ* result = NULL;
	if (v.first) {
		result = reinterpret_cast<XYZ*>(v.second);
	}
	return std::make_pair(v.first, result);
}

std::pair<PyObject*, XYZ*> newXYZArray(npy_intp n, bool zero) {
	std::pair<PyObject*, XYZ*> result = newXYZArray(n);
	if (result.second != NULL && zero) {
		memset(result.second, 0, sizeof(XYZ)*n);
	}
	return result;
}

std::pair<PyObject*, XYZ*> newXYZArray(npy_intp n, const XYZ& v0) {
	std::pair<PyObject*, RealNumber*> v = newRealArray(n*3);
	XYZ* result = NULL;
	if (v.first) {
		result = reinterpret_cast<XYZ*>(v.second);
		for (size_t i = 0; i < n; ++i) {
			result[i] = v0;
		}
	}
	return std::make_pair(v.first, result);
}

std::pair<PyObject*, XYZ*> newXYZArray(PyObject* model, const char szAttrName[], size_t n) {
	std::pair<PyObject*, RealNumber*> result = newRealArray(model, szAttrName, n*3);
	if (result.first != NULL) {
		return std::make_pair(result.first, reinterpret_cast<XYZ*>(result.second));
	}
	else {
		return std::pair<PyObject*, XYZ*>(NULL, NULL);
	}
}



} }
%}
